// Copyright (c) Microsoft Corporation. All rights reserved.

namespace Microsoft.Tools.CodeRunner.UnitTest
{
	using System;
	using System.IO;
	using Microsoft.VisualStudio.TestTools.UnitTesting;
	using Microsoft.Tools.CodeRunner;
	
	[TestClass] 
	public class ParsedPathTests
	{
		[TestMethod] public void TestOperators()
		{
			ParsedPath pp = new ParsedPath("file.txt");
			
			Assert.IsFalse(pp == null);
			Assert.IsFalse(null == pp);
			Assert.IsTrue(pp != null);
			Assert.IsTrue(null != pp);
			Assert.IsFalse(pp.Equals(null));
			Assert.IsFalse(ParsedPath.Equals(pp, null));
			Assert.IsFalse(ParsedPath.Equals(null, pp));
			
			int hash = pp.GetHashCode();
			
			Assert.IsFalse(pp == ParsedPath.Empty);
		}

		[TestMethod] public void ConstructParsedPath() 
		{
			// Test some good paths
			AssertPathParts(@"\", PathType.Automatic, "", "", @"", @"\", "", "");
			AssertPathParts(@".txt", PathType.Automatic, "", "", "", "", "", ".txt");
			AssertPathParts(@"*.txt", PathType.File, "", "", "", "", @"*", @".txt");
			AssertPathParts(@"..\*.txt", PathType.Automatic, "", "", "", @"..\", @"*", @".txt");
			AssertPathParts(@".", PathType.Automatic, "", "", "", "", "", "");
			AssertPathParts(@".", PathType.Directory, "", "", "", @".\", "", "");
			AssertPathParts(@"..", PathType.Automatic, "", "", "", "", "", "");
			AssertPathParts(@"..", PathType.Directory, "", "", "", @"..\", "", "");
			
			AssertPathParts(@"c:", PathType.Automatic, "", "", @"c:", "", "", "");
			AssertPathParts(@"c:", PathType.RootOnly, "", "", @"c:", "", "", "");
			AssertPathParts(@"c:\", PathType.Directory, "", "", @"c:", @"\", "", "");
			AssertPathParts(@"c:foo.txt", PathType.Automatic, "", "", @"c:", "", "foo", ".txt");
			AssertPathParts(@"c:.txt", PathType.Automatic, "", "", @"c:", "", "", ".txt");
			AssertPathParts(@"c:....txt", PathType.Automatic, "", "", @"c:", "", "...", ".txt");
			AssertPathParts(@"c:foo.txt", PathType.Directory, "", "", @"c:", @"foo.txt\", "", "");
			AssertPathParts(@"c:blah\blah\", PathType.Automatic, "", "", @"c:", @"blah\blah\", "", "");
			AssertPathParts(@"c:blah\blah", PathType.Directory, "", "", @"c:", @"blah\blah\", "", "");
			AssertPathParts(@"c:blah\blah", PathType.Automatic, "", "", @"c:", @"blah\", "blah", "");
			AssertPathParts(@"c:\test", PathType.Directory, "", "", @"c:", @"\test\", @"", "");
			AssertPathParts(@"c:\test", PathType.File, "", "", @"c:", @"\", @"test", "");
			AssertPathParts(@"c:\test.txt", PathType.File, "", "", @"c:", @"\", @"test", @".txt");
			AssertPathParts(@"c:\whatever\test.txt", PathType.File, "", "", @"c:", @"\whatever\", @"test", @".txt");
			AssertPathParts(@"c:\test\..\temp\??.txt", PathType.File, "", "", @"c:", @"\test\..\temp\", @"??", @".txt");
			AssertPathParts(@"C:/test\the/use of\forward/slashes\a.txt", PathType.Automatic, 
				"", "", @"C:", @"\test\the\use of\forward\slashes\", "a", @".txt");
			AssertPathParts(@"   C:\ remove \  trailing \     spaces   \  file.txt   ", PathType.Automatic, 
				"", "", @"C:", @"\ remove\  trailing\     spaces\", "  file", @".txt");
			AssertPathParts(@"C:\remove.\trailing....\dots . .\and\spaces. . \file.txt. . .", PathType.Automatic, 
				"", "", @"C:", @"\remove\trailing\dots\and\spaces\", "file", @".txt");
			AssertPathParts(@"  .  . a . really . strange . name .. . . \a\b\c\...", PathType.Directory, 
				"", "", "", @".  . a . really . strange . name\a\b\c\...\", "", "");
			AssertPathParts(@"  ""  C:\this path is quoted\file.txt  ""  ", PathType.Automatic, 
				"", "", @"C:", @"\this path is quoted\", "file", @".txt");
			AssertPathParts(@"c:\assembly\Company.Product.Subcomponent.dll", PathType.Automatic, 
				"", "", @"c:", @"\assembly\", "Company.Product.Subcomponent", @".dll");
			
			AssertPathParts(@"\\machine\share", PathType.Automatic, @"\\machine", @"\share", "", "", "", "");
			AssertPathParts(@"\\machine\share\", PathType.Automatic, @"\\machine", @"\share", "", @"\", @"", @"");
			AssertPathParts(@"\\computer\share\test\subtest\abc.txt", PathType.Automatic, 
				@"\\computer", @"\share", "", @"\test\subtest\", @"abc", @".txt");

			// Test special last folder property
			Assert.AreEqual("c", new ParsedPath(@"c:\a\b\c\", PathType.Automatic).DirectoryLastFolderNoSeparator);
			Assert.AreEqual("", new ParsedPath(@"c:\", PathType.Automatic).DirectoryLastFolderNoSeparator);

			// Test some bad paths
			AssertBadPath(@":", PathType.File);
			AssertBadPath(@"\", PathType.File);
			AssertBadPath(@"\  \", PathType.Directory);
			AssertBadPath(@"  ""    ""  ", PathType.Automatic);
			AssertBadPath(@"c:\*&#()_@\~{}|<>", PathType.Automatic);

			AssertBadPath(@"c:", PathType.Directory);
			AssertBadPath(@"c: \file.txt", PathType.File);
			AssertBadPath(@"c:\a\b\*\c\", PathType.Automatic);
			AssertBadPath(@"c:\dir\file.txt", PathType.RootOnly);
			AssertBadPath(@"c:\dir\\file.txt", PathType.File);

			AssertBadPath(@"\\", PathType.RootOnly);
			AssertBadPath(@"\\computer", PathType.RootOnly);
			AssertBadPath(@"\\computer\", PathType.RootOnly);
			AssertBadPath(@"\\computer\\", PathType.RootOnly);
			AssertBadPath(@"\\machine\share\\bad", PathType.Directory);
		}

		[TestMethod] public void MakeFullPath()
		{
			// Test some good paths
			AssertPathPartsFull(@".txt", 
				@"c:\temp\", "", "", @"c:", @"\temp\", "", ".txt");
			AssertPathPartsFull(@"c:\test\..\temp\??.txt", 
				null, "", "", @"c:", @"\temp\", @"??", @".txt");
			AssertPathPartsFull(@"\test\..\temp\abc.txt", 
				@"c:\a\b\", "", "", "c:", @"\temp\", @"abc", @".txt");
			AssertPathPartsFull(@"test\..\temp\abc.txt", 
				@"c:\a\b\", "", "", @"c:", @"\a\b\temp\", @"abc", @".txt");
			AssertPathPartsFull(@".\test\....\temp\abc.txt", 
				@"c:\a\b\c\", "", "", "c:", @"\a\temp\", @"abc", @".txt");
			AssertPathPartsFull(@"...\test\abc.txt", 
				@"c:\a\b\c\", "", "", "c:", @"\a\test\", @"abc", @".txt");
			AssertPathPartsFull(@"C:\temp\..yes...this...is.a..legal.file.name....\and...\so...\...is\.this.\blah.txt", 
				null, "", "", @"C:", 
				@"\temp\..yes...this...is.a..legal.file.name\and\so\...is\.this\", 
				@"blah", @".txt");

			// Test that using the current directory works
			ParsedPath pp = new ParsedPath(Environment.CurrentDirectory, PathType.Directory);
			AssertPathPartsFull(@"test.txt", null, pp.Machine, pp.Share, pp.Drive, pp.Directory, "test", ".txt");

			// Test some bad paths
			AssertBadPathFull(@"c:\test\..\..\temp\abc.txt", null);  // Too many '..'s
			AssertBadPathFull(@"test\......\temp\.\abc.txt", @"c:\");  // Too many '....'s
		}
		
		[TestMethod] public void MakeRelativePath()
		{
            AssertPathPartsRelative(@"c:\a\p.q", @"c:\a\", @".\");
            AssertPathPartsRelative(@"c:\a\", @"c:\a\b\c\p.q", @"..\..\"); 
			AssertPathPartsRelative(@"c:\a\b\c\p.q", @"c:\a\", @".\b\c\"); 
			AssertPathPartsRelative(@"c:\a\b\c\p.q", @"c:\a\x\y\", @"..\..\b\c\");

            AssertBadPathPartsRelative(@"..\a.txt", @"c:\a\b");
            AssertBadPathPartsRelative(@"a.txt", @"b");
        }

		[TestMethod] public void MakeParentPath()
		{
			// Test going up one parent
			AssertParentPath(@"c:\a\b\c\p.q", -1, null, "", "", "c:", @"\a\b\", "p", ".q"); 
			AssertParentPath(@"c:\a\b\c\", -1, null, "", "", "c:", @"\a\b\", "", ""); 
			AssertParentPath(@"c:\a\b\..\c\", -1, null, "", "", "c:", @"\a\", "", ""); 
			AssertParentPath(@"..\c\", -1, @"c:\a\b\", "", "", "c:", @"\a\", "", ""); 
			AssertParentPath(@"\\machine\share\a\b\c\..\d\foo.bar", -1, null, 
				@"\\machine", @"\share", "", @"\a\b\", "foo", ".bar"); 
			
			// Test going up multiple parents
			AssertParentPath(@"c:\a\b\c\d\e\", -3, null, "", "", "c:", @"\a\b\", "", "");
			AssertParentPath(@"c:\a\b\c\d\e\", -5, null, "", "", "c:", @"\", "", "");

			// Test bad stuff
			AssertBadParentPath(@"c:\", -1); // Already root
			AssertBadParentPath(@"c:\a\", 2); // Positive index not allowed
			AssertBadParentPath(@"c:\a\b\c\", -4); // Too many parent levels
		}

		#region Assert paths parts
		private void AssertPathParts(
			string path,
			PathType type, 
			string machine,
			string share,
			string drive,
			string directory,
			string file,
			string extension)
		{
			ParsedPath pp = new ParsedPath(path, type);
		
			Assert.AreEqual(machine, pp.Machine);
			Assert.AreEqual(share, pp.Share);
			Assert.AreEqual(drive, pp.Drive);
			Assert.AreEqual(directory, pp.Directory);
			Assert.AreEqual(file, pp.File);
			Assert.AreEqual(extension, pp.Extension);
		}
		
		private void AssertBadPath(
			string path, 
			PathType type)
		{
			try
			{
				ParsedPath pp = new ParsedPath(path, type);
				Assert.Fail("Badly formed path not caught");
			}
			catch (Exception e)
			{
				Assert.IsTrue(e is ArgumentException);
			}
		}
		#endregion		

		#region Assert path parts with MakeFullPath()
		private void AssertPathPartsFull(
			string path,
			string baseDir,
			string machine,
			string share,
			string drive,
			string directory,
			string file,
			string extension)
		{
			ParsedPath pp;
			
			if (baseDir != null)
				pp = new ParsedPath(path, PathType.Automatic).MakeFullPath(new ParsedPath(baseDir));
			else
				pp = new ParsedPath(path, PathType.Automatic).MakeFullPath();
		
			Assert.AreEqual(machine, pp.Machine);
			Assert.AreEqual(share, pp.Share);
			Assert.AreEqual(drive, pp.Drive);
			Assert.AreEqual(directory, pp.Directory);
			Assert.AreEqual(file, pp.File);
			Assert.AreEqual(extension, pp.Extension);
		}
		
		private void AssertBadPathFull(
			string path, 
			string baseDir)
		{
			try
			{
				ParsedPath pp = new ParsedPath(path, PathType.Automatic).MakeFullPath(
					baseDir == null ? null : new ParsedPath(baseDir));
				Assert.Fail("Badly formed path not caught");
			}
			catch (Exception e)
			{
				Assert.IsTrue(e is ArgumentException);
			}
		}
		#endregion

		#region Assert path directory with MakeRelativePath()
		private void AssertPathPartsRelative(
            string path,
            string basePath,
			string directory)
		{
			ParsedPath pp = new ParsedPath(path, PathType.Automatic).MakeRelativePath(new ParsedPath(basePath));
			Assert.AreEqual(directory, pp.Directory);
		}

        private void AssertBadPathPartsRelative(
            string path,
            string basePath)
        {
            try
            {
                ParsedPath pp = new ParsedPath(path, PathType.Automatic).MakeRelativePath(new ParsedPath(basePath));
                Assert.Fail("MakeRelativePath succeeded and should have failed");
            }
            catch (Exception e)
            {
                Assert.IsTrue(e is ArgumentException);
            }
        }
        #endregion

		#region Assert path directory with MakeParentPath()
		private void AssertParentPath(
			string path,
			int level,
			string baseDir,
			string machine,
			string share,
			string drive,
			string directory,
			string file,
			string extension)
		{
			ParsedPath pp;
			
			// Test out specific entry points based on the values passed in
			if (level < -1)
			{
				if (baseDir != null)
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath(level, new ParsedPath(baseDir));
				else
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath(level);
			
				if (pp == null)
				{
					Assert.IsNull(directory, "Expected result was not null");
					return;
				}
			}
			else
			{
				if (baseDir != null)
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath(new ParsedPath(baseDir));
				else
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath();

				if (pp == null)
				{
					Assert.IsNull(directory, "Expected result was not null");
					return;
				}
			}
					
			Assert.AreEqual(machine, pp.Machine);
			Assert.AreEqual(share, pp.Share);
			Assert.AreEqual(drive, pp.Drive);
			Assert.AreEqual(directory, pp.Directory);
			Assert.AreEqual(file, pp.File);
			Assert.AreEqual(extension, pp.Extension);
		}

		private void AssertBadParentPath(
			string path,
			int level)
		{
			try
			{
				ParsedPath pp;
				
				if (level <= -1 || level > 0)
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath(level);
				else
					pp = new ParsedPath(path, PathType.Automatic).MakeParentPath();
					
				Assert.Fail("Get parent succeeded and should have failed");
			}
			catch (Exception e)
			{
				Assert.IsTrue(e is ArgumentException);
			}
		}
		#endregion

		[TestMethod] public void TestPathTypes()
		{
			Assert.IsTrue(new ParsedPath(@"c:\temp\").IsDirectory);
			Assert.IsFalse(new ParsedPath(@"c:\temp").IsDirectory);
			Assert.IsTrue(new ParsedPath(@"\\machine\share\foo").HasUncRoot);
			Assert.IsFalse(new ParsedPath(@"c:\foo").HasUncRoot);
			Assert.IsTrue(new ParsedPath(@"\\machine\share\foo\*.t?t").HasWildcards);
			Assert.IsFalse(new ParsedPath(@"\\machine\share\foo\foo.txt").HasWildcards);
			Assert.IsTrue(new ParsedPath(@"\\machine\share\foo\test.txt").IsRooted);
			Assert.IsFalse(new ParsedPath(@"foo\test.txt").IsRooted);
			Assert.IsTrue(new ParsedPath(@"C:\share\foo\").HasDriveRoot);
			Assert.IsFalse(new ParsedPath(@"\\machine\share\foo\").HasDriveRoot);
			Assert.IsTrue(new ParsedPath(@"C:\share\foo\..\..\thing.txt").IsRelative);
			Assert.IsTrue(new ParsedPath(@"C:\share\foo\...\thing.txt").IsRelative);
			Assert.IsTrue(new ParsedPath(@"...\thing.txt").IsRelative);
			Assert.IsFalse(new ParsedPath(@"\\machine\share\foo\thing.txt").IsRelative);
			Assert.IsTrue(new ParsedPath(@"C:\share\foo\thing.txt").IsFull);
			Assert.IsFalse(new ParsedPath(@"\thing.txt").IsFull);
			Assert.IsFalse(new ParsedPath(@"c:\a\..\thing.txt").IsFull);
		}

		[TestMethod] public void TestSubDirectories()
		{
			Assert.AreEqual(4, new ParsedPath(@"c:\a\b\c\").DirectoryDepth);
			Assert.AreEqual(4, new ParsedPath(@"\\machine\share\a\b\c\").DirectoryDepth);
			
			string[] subDirs;
			
			subDirs = new ParsedPath(@"c:\a\b\c\").SubDirectories;
			
			Assert.AreEqual(4, subDirs.Length);
			Assert.AreEqual(Path.DirectorySeparatorChar.ToString(), subDirs[0]);
			Assert.AreEqual("c", subDirs[3]);
			
			subDirs = new ParsedPath(@"c:\").SubDirectories;
			
			Assert.AreEqual(1, subDirs.Length);
			Assert.AreEqual(Path.DirectorySeparatorChar.ToString(), subDirs[0]);
		}
	}
}